#include "StdAfx.h"
#include "d3dx9math.h"
#include "DebugAirScape.h"
#include "TestApplication.h"
#include "AppTestCamera.h"

#include "chunkspace.h"
#include "chunkmanager.h"

#include "StringUtility.h"

using namespace ChunkSystem;
using namespace Common;

AirScapeCamera::AirScapeCamera()
{
	D3DXVECTOR3	vEyePt( 0.0f, 0.0f, 0.0f );
	D3DXVECTOR3	vLookAtPt( 0.0f, -10.0f, 0.0f );

	SetViewParams( &vEyePt, &vLookAtPt );
}

AirScapeCamera::~AirScapeCamera()
{
}

void  AirScapeCamera::SetViewParams( const D3DXVECTOR3* pvEyePt, const D3DXVECTOR3* pvLookatPt )
{
	if( NULL == pvEyePt || NULL == pvLookatPt )
		return;

	m_vEye = *pvEyePt;
	m_vLookAt = *pvLookatPt;

	// Calc the view matrix
	D3DXVECTOR3 vUp( 0,0,1 );
	D3DXMatrixLookAtLH( &m_mView, pvEyePt, pvLookatPt, &vUp );
}

//--------------------------------------------------------------------------------------
// Calculates the projection matrix based on input params
//--------------------------------------------------------------------------------------
VOID AirScapeCamera::SetProjParams( FLOAT fWidth, FLOAT fHeight, FLOAT fNearPlane, FLOAT fFarPlane )
{
	// Set attributes for the projection matrix
	m_fWidth = fWidth;
	m_fHeight = fHeight;
	m_fNearPlane = fNearPlane;
	m_fFarPlane = fFarPlane;

	D3DXMatrixOrthoLH( &m_mProj, m_fWidth, m_fHeight, fNearPlane, fFarPlane );
}

///////////////////////////////////////////////////////////////////////////////
DebugAirScape::DebugAirScape(void)
	:m_pRenderTarger(NULL),m_fHeight(1000.0f),m_pFont(NULL),m_pTextSprite(NULL)
{
}


DebugAirScape::~DebugAirScape(void)
{
	if( m_pRenderTarger != NULL )
		m_pRenderTarger->Release();
}

bool DebugAirScape::Reset( IDirect3DDevice9* pDevice )
{
	if( m_pRenderTarger != NULL )
		m_pRenderTarger->Release();

	return this->Init( pDevice );
}

bool DebugAirScape::Init( IDirect3DDevice9* pDevice )
{
	if( pDevice == NULL )
		return false;

	HRESULT hr;
	IDirect3DSurface9* pBackBuffer;
	hr = pDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer );
	if( SUCCEEDED( hr ) )
	{
		pBackBuffer->GetDesc( &m_rBBufferSurfaceDesc );
		if( pBackBuffer )
			pBackBuffer->Release();
	}


	//[ 0 ] CREATE RENDER TARGET
	//------------------------------
	if( FAILED( pDevice->CreateTexture(
		m_rBBufferSurfaceDesc.Width / 2, m_rBBufferSurfaceDesc.Height / 2,
		1, D3DUSAGE_RENDERTARGET, m_rBBufferSurfaceDesc.Format,
		D3DPOOL_DEFAULT, &m_pRenderTarger, NULL
		) ) )
		return false;

    if( FAILED( D3DXCreateSprite( pDevice, &m_pTextSprite ) ) )
		return false;


	if( FAILED( D3DXCreateFont( pDevice, 8, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET,
                              OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
                              L"Arial", &m_pFont ) ) )
		return false;

	pDevice->SetSamplerState( 0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR );
	pDevice->SetSamplerState( 0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR );

	return true;
}

void DebugAirScape::UpdateCamera( const D3DXVECTOR3* pLookAt )
{
	D3DXVECTOR3 vEye( pLookAt->x, pLookAt->y + m_fHeight, pLookAt->z );

	m_Camera.SetViewParams(&vEye,pLookAt);

	m_Camera.SetProjParams( m_rBBufferSurfaceDesc.Width * 1.5f , m_rBBufferSurfaceDesc.Height * 1.5f , 1 , 5000 ); 
}

const AirScapeCamera& DebugAirScape::GetCamera() const
{
	return m_Camera;
}

//render to a quad, just for practice ;-)
struct QuadVertexData
{
	D3DXVECTOR4 vPos;
	D3DXVECTOR2	vUV;
};

#define FVF_QUAD (D3DFVF_XYZRHW | D3DFVF_TEX1)

bool DebugAirScape::Draw( IDirect3DDevice9* pDevice, TestCamera* pChunkCamera)
{
	if( pDevice == NULL || pChunkCamera == NULL )
		return false;

	pDevice->SetTexture( 0, NULL );
	LPDIRECT3DSURFACE9 pPrevSurf = NULL;
	if( FAILED( pDevice->GetRenderTarget( 0, &pPrevSurf ) ) )
	{
		OutputDebugString( L"Could not retrieve a reference to the previous render target.\n"
			);
		return false;
	}

	LPDIRECT3DSURFACE9 pRTSurf = NULL;
	if( FAILED( m_pRenderTarger->GetSurfaceLevel( 0, &pRTSurf ) ) )
	{
		OutputDebugString( L"Could not get the top level surface for the render target\n"
			);
		return false;
	}

	if( FAILED( pDevice->SetRenderTarget( 0, pRTSurf ) ) )
	{
		OutputDebugString( L"Could not set the birdscape texture as a new render target.\n" );
		return false;
	}
	pDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x0fffffff , 1.0f, 0 );

	D3DXMATRIX matIdentity;
	pDevice->SetTransform(D3DTS_WORLD, D3DXMatrixIdentity(&matIdentity) );
	//draw process
	ChunkManager::GetInstance().Draw();
	pDevice->SetTransform(D3DTS_WORLD, D3DXMatrixIdentity(&matIdentity) );
	//draw text
	_DrawText();
	pDevice->SetTransform(D3DTS_WORLD, D3DXMatrixIdentity(&matIdentity) );
	//draw indicator
	_DrawIndicator(pDevice,pChunkCamera);
	pDevice->SetTransform(D3DTS_WORLD, D3DXMatrixIdentity(&matIdentity) );

	pDevice->SetRenderTarget( 0, pPrevSurf );

	pDevice->SetTexture( 0, m_pRenderTarger );

	QuadVertexData VBData[4] = 
	{
		{ D3DXVECTOR4( 0.0f,0.0f,0.0f,1.0f),							 D3DXVECTOR2(0.0f,0.0f) },
		{ D3DXVECTOR4( m_rBBufferSurfaceDesc.Width , 0.0f,0.0f,1.0f),  D3DXVECTOR2(1.0f,0.0f) },
		{ D3DXVECTOR4( 0.0f,m_rBBufferSurfaceDesc.Height ,0.0f,1.0f),  D3DXVECTOR2(0.0f,1.0f) },
		{ D3DXVECTOR4( m_rBBufferSurfaceDesc.Width ,m_rBBufferSurfaceDesc.Height ,0.0f,1.0f),  D3DXVECTOR2(1.0f,1.0f) }
	};
	UINT16 IBData[6] = 
	{
		0 , 1 , 2 ,
		3 , 2 , 1
	};

	pDevice->SetFVF(FVF_QUAD);

	pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE , TRUE );
	pDevice->SetRenderState( D3DRS_SRCBLEND  , D3DBLEND_SRCALPHA );
	pDevice->SetRenderState( D3DRS_DESTBLEND , D3DBLEND_INVSRCALPHA  );

	if( FAILED( pDevice->DrawIndexedPrimitiveUP( D3DPT_TRIANGLELIST, 0, 4 ,2, IBData ,D3DFMT_INDEX16 , VBData, sizeof(QuadVertexData) ) ) )
	{
		OutputDebugString( L"quad vb creating failed.\n" );
		return false;
	}

	if( pRTSurf )
		pRTSurf->Release();

	if( pPrevSurf )
		pPrevSurf->Release();

	pDevice->SetTexture( 0, NULL );

	pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE , FALSE );
	pDevice->SetRenderState( D3DRS_SRCBLEND  , D3DBLEND_ONE );
	pDevice->SetRenderState( D3DRS_DESTBLEND , D3DBLEND_ZERO );

	return true;

}

void DebugAirScape::_DrawText(void)
{
	const D3DXMATRIX* pMatView = m_Camera.GetViewMatrix();
	const D3DXMATRIX* pMatProj = m_Camera.GetProjMatrix();

	ChunkSpace* pCurChunkSpace = ChunkManager::GetInstance().GetCurChunkSpace();
	if( pCurChunkSpace == NULL )
		return;

	m_pTextSprite->Begin( D3DXSPRITE_ALPHABLEND );

	ChunkSpace::CurrentChunkMap& curChunk = pCurChunkSpace->GetCurrentChunk();
	for( ChunkSpace::CurrentChunkMap::iterator itrChunk = curChunk.begin(); itrChunk != curChunk.end() ; ++itrChunk )
	{
		if( itrChunk->second->GetCurState() != Chunk::eLoaded && itrChunk->second->GetCurState() != Chunk::eBinded )
			continue;

		AxisAlignedBox aabb = itrChunk->second->GetAABB();
		Vector3 aabbCenter = aabb.getCenter();
		D3DXVECTOR3 vecPoint( aabbCenter.x, aabbCenter.y, aabbCenter.z);

		D3DXMATRIX	matFinal;
		D3DXMatrixMultiply( &matFinal, pMatView , pMatProj );

		D3DXVECTOR3 vOutPut;
		D3DXVec3TransformCoord( &vOutPut , &vecPoint, &matFinal );

		if( vOutPut.x > 1.0f || vOutPut.y > 1.0f ||  vOutPut.x < -1.0f || vOutPut.y < -1.0f || vOutPut.z < 0.0f )
			continue;

		//transform to screen space
		int iOffsetX = ( vOutPut.x + 1 ) / 4 * m_rBBufferSurfaceDesc.Width * 0.95;
		int iOffsetY = ( 1 - vOutPut.y ) / 4 * m_rBBufferSurfaceDesc.Height;

		WCHAR szChunkID[10];
		if( !StringUtility::MByteToWChar( itrChunk->second->GetId().c_str(), szChunkID , 10) )
			continue;

		RECT rectFont;
		SetRect( &rectFont, iOffsetX, iOffsetY, 0, 0 );

		m_pFont->DrawTextW( m_pTextSprite, szChunkID, -1, &rectFont , DT_NOCLIP, 0xffffffff );

	}

	m_pTextSprite->End();
}

struct CircleVertex
{
	enum{ FVF = D3DFVF_XYZ|D3DFVF_DIFFUSE };

	D3DXVECTOR3	vPos;
	DWORD		dColor;
};

void DebugAirScape::_DrawIndicator( IDirect3DDevice9* pDevice, TestCamera* pChunkCamera )
{
	//circle construct
	float fCircleRadius = 1.0f;
	CircleVertex vCirclePoints[37];
	for( int iX = 0; iX != 37; ++iX )
	{
		vCirclePoints[iX].vPos.x = fCircleRadius * ::sin( 2 * Math::PI / 36.0f * iX );
		vCirclePoints[iX].vPos.z = fCircleRadius * ::cos( 2 * Math::PI / 36.0f * iX );
		vCirclePoints[iX].vPos.y = 0.0f;

		vCirclePoints[iX].dColor = 0xffffffff;
	}

	static float fAnimation = 0.0f;

	D3DXMATRIX	matWorldCircle;
	
	D3DXMATRIX	matCameraPos;
	const D3DXVECTOR3* vCameraPos = m_Camera.GetLookAt();
	D3DXMATRIX	matCircleScale;
	D3DXMatrixIdentity( &matCircleScale );
	D3DXMatrixScaling( &matCircleScale, 10.0f, 10.0f, 10.0f );
	D3DXMatrixTranslation( &matCameraPos, vCameraPos->x, vCameraPos->y, vCameraPos->z );

	D3DXMatrixMultiply(&matWorldCircle, &matCircleScale, &matCameraPos );

	pDevice->SetFVF( CircleVertex::FVF );

	pDevice->SetTransform( D3DTS_WORLD, &matWorldCircle );

	pDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, 36, vCirclePoints, sizeof(CircleVertex) );

	//draw loading range
	float fLoadingRange = pChunkCamera->GetLoadingRange();
	D3DXMatrixIdentity( &matCircleScale );
	D3DXMatrixScaling( &matCircleScale, fLoadingRange, fLoadingRange, fLoadingRange );
	D3DXMatrixMultiply(&matWorldCircle, &matCircleScale, &matCameraPos );
	pDevice->SetTransform( D3DTS_WORLD, &matWorldCircle );

	for( int iX = 0; iX != 37; ++iX )
		vCirclePoints[iX].dColor = 0xff0000f0;

	pDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, 36, vCirclePoints, sizeof(CircleVertex) );

	//draw holding range
	float fHoldingRange = pChunkCamera->GetHoldingRange();
	D3DXMatrixIdentity( &matCircleScale );
	D3DXMatrixScaling( &matCircleScale, fHoldingRange, fHoldingRange, fHoldingRange );
	D3DXMatrixMultiply(&matWorldCircle, &matCircleScale, &matCameraPos );
	pDevice->SetTransform( D3DTS_WORLD, &matWorldCircle );

	for( int iX = 0; iX != 37; ++iX )
		vCirclePoints[iX].dColor = 0xff00ff00;

	pDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, 36, vCirclePoints, sizeof(CircleVertex) );

}